#include "../DomainSocket.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

// TODO: Move this to central header file
#include <string.h>

namespace Greatewall
{
    namespace Network
    {
        SysSockHandler
        DomainSocket::createDomainSock()
        {
            return socket(AF_UNIX, SOCK_STREAM, 0);
        }

        bool
        DomainSocket::send(const char* buf, ssize_t& size, bool non_block)
        {
            if (!_inited)
            {
                // TODO: Error handling
                return false;
            }

            if (_mode == SERVER)
            {
                // TODO: Error handling
                return false;
            }

            return IpSocket::send(buf, size, non_block);
        }

        bool
        DomainSocket::recv(char*& buf, ssize_t& size, bool non_block)
        {
            if (!_inited)
            {
                return false;
            }

            if (_mode == SERVER)
            {
                // TODO: Error handling
                return false;
            }

            return IpSocket::recv(buf, size, non_block);
        }

        bool
        DomainSocket::initDomainSock(std::string& sock_path, DomainSockMode mode, int backlog)
        {
            if (_inited)
            {
                // TODO: Error handling
                return false;
            }

            if (_hdl < 0 && ((_hdl = createDomainSock()) < 0))
            {
                // TODO: Error handling
                return false;
            }

            if (sock_path.empty())
            {
                // TODO: Error handling
                return false;
            }

            sockaddr_un sock;
            memset(&sock, 0, sizeof(sock));

            int copy_size = sizeof(sock.sun_path) - 1 > sock_path.length()
                ? sock_path.length() : sizeof(sock.sun_path) - 1;
            sock.sun_family     = AF_UNIX;
            strncpy(sock.sun_path, sock_path.c_str(), copy_size);

            switch (mode)
            {
                default:
                {
                    // Unsupported mode
                    // TODO: Error handling
                    return false;
                }
                case CLIENT:
                {
                    if (connect(_hdl, (sockaddr*)&sock, sizeof(sock)) < 0)
                    {
                        // TODO: Error handling
                        return false;
                    }
                    break;
                }
                case SERVER:
                {
                    int flag_on = 1;
                    if (setsockopt(_hdl, SOL_SOCKET, SO_REUSEADDR, &flag_on, sizeof(int)) == -1)
                    {
                        // TODO: Error handling
                        return false;
                    }

                    if (bind(_hdl, (sockaddr*)&sock, sizeof(sock)) == -1)
                    {
                        // TODO: Error handling
                        return false;
                    }

                    if (listen(_hdl, backlog) == -1)
                    {
                        // TODO: Error handling
                        return false;
                    }

                    break;
                }
            }

            _sock_path = sock_path;
            _mode      = mode;
            _inited    = true;

            return true;
        }

        bool
        DomainSocket::initDomainSock(SysSockHandler hdl, std::string& sock_path)
        {
            if (_inited)
            {
                // TODO: Error handling
                return false;
            }

            _mode       = CLIENT_HANDLER;
            _hdl        = hdl;
            _sock_path  = sock_path;
            _inited     = true;

            return true;
        }

        void
        DomainSocket::close()
        {
            if (_hdl > 0)
            {
                ::close(_hdl);
                _hdl = -1;
                _inited = false;
            }
        }
    }
}

